home *** CD-ROM | disk | FTP | other *** search
- /*
- * Buffer management for public domain tar.
- *
- * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
- * MS-DOS port 2/87 by Eric Roskos.
- * Minix port 3/88 by Eric Roskos.
- *
- * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
- */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h> /* For non-Berkeley systems */
- #include <fcntl.h>
- #include <signal.h>
-
- #include "tar.h"
- #include "port.h"
-
- #define STDIN 0 /* Standard input file descriptor */
- #define STDOUT 1 /* Standard output file descriptor */
-
- #define PREAD 0 /* Read file descriptor from pipe() */
- #define PWRITE 1 /* Write file descriptor from pipe() */
-
- extern char *valloc();
-
- /*
- * V7 doesn't have a #define for this.
- */
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
-
- #define MAGIC_STAT 105 /* Magic status returned by child, if it
- * can't exec compress. We hope compress
- * never returns this status! */
- /*
- * The record pointed to by save_rec should not be overlaid
- * when reading in a new tape block. Copy it to record_save_area first, and
- * change the pointer in *save_rec to point to record_save_area.
- * Saved_recno records the record number at the time of the save.
- * This is used by annofile() to print the record number of a file's
- * header record.
- */
- static union record **save_rec;
- static union record record_save_area;
- static int saved_recno;
-
- /*
- * PID of child compress program, if f_compress.
- */
- static int compress_pid;
-
- /*
- * Record number of the start of this block of records
- */
- static int baserec;
-
- /*
- * Error recovery stuff
- */
- static int r_error_count;
-
-
- /*
- * Return the location of the next available input or output record.
- */
- union record *
- findrec()
- {
- if (ar_record == ar_last)
- {
- flush_archive();
- if (ar_record == ar_last)
- return (union record *) NULL; /* EOF */
- }
- return ar_record;
- }
-
-
- /*
- * Indicate that we have used all records up thru the argument.
- * (should the arg have an off-by-1? XXX FIXME)
- */
- void
- userec(rec)
- union record *rec;
- {
- while (rec >= ar_record)
- ar_record++;
-
- /*
- * Do NOT flush the archive here. If we do, the same argument to
- * userec() could mean the next record (if the input block is exactly one
- * record long), which is not what is intended.
- */
- if (ar_record > ar_last)
- abort();
- }
-
-
- /*
- * Return a pointer to the end of the current records buffer.
- * All the space between findrec() and endofrecs() is available
- * for filling with data, or taking data from.
- */
- union record *
- endofrecs()
- {
- return ar_last;
- }
-
-
- /*
- * Open an archive file. The argument specifies whether we are
- * reading or writing.
- *
- * With DOS, we ALWAYS open the archive in binary mode: whether or not
- * to do CRLF translations depends on whether we open the input files
- * as binary or ASCII, but we always write the archive without making
- * any translations from what this program saw when it did the write.
- */
- open_archive(read)
- int read;
- {
-
- if (ar_file[0] == '-' && ar_file[1] == '\0')
- {
- if (read)
- archive = STDIN;
- else
- archive = STDOUT;
- }
- else
- if (read)
- {
- #ifdef MSDOS
- archive = 9999; /* for debugging - invalid fd to cause err */
-
- if (!f_phys) /* don't open if we're doing direct drive I/O */
- #endif
- archive = open(ar_file, O_RDONLY
- #ifdef MSDOS
- | O_BINARY
- #endif
- );
- }
- else
- {
- #ifdef MSDOS
- archive = 9999;
-
- if (!f_phys)
- #endif
- #ifdef V7
- archive = creat(ar_file, 0666);
- #else
- archive = open(ar_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
- 0666);
- #endif
- }
-
- if (archive < 0)
- {
- perror(ar_file);
- exit(EX_BADARCH);
- }
-
- /* NOSTRICT */
- ar_block = (union record *) valloc((unsigned) blocksize);
- if (!ar_block)
- {
- fprintf(stderr,
- "tar: could not allocate memory for blocking factor %d\n",
- blocking);
- exit(EX_ARGSBAD);
- }
-
- ar_record = ar_block;
- ar_last = ar_block + blocking;
-
- /*
- * Handle compressed archives.
- *
- * FIXME, currently supported for reading only. FIXME, writing involves
- * forking again for a small process that will reblock the output of
- * compress to the user's specs.
- */
- #ifndef MSDOS
- if (f_compress)
- {
- int pipes[2];
- int err;
-
- if (!read)
- {
- fprintf(stderr,
- "tar: cannot write compressed archives yet.\n");
- exit(EX_ARGSBAD);
- }
-
- /* Create a pipe to get compress's output to us */
- err = pipe(pipes);
- if (err < 0)
- {
- perror("tar: cannot create pipe to compress");
- exit(EX_SYSTEM);
- }
-
- /* Fork compress process */
- compress_pid = fork();
- if (compress_pid < 0)
- {
- perror("tar: cannot fork compress");
- exit(EX_SYSTEM);
- }
-
- /*
- * Child process.
- *
- * Move input to stdin, write side of pipe to stdout, then exec
- * compress.
- */
- if (compress_pid == 0)
- {
- (void) close(pipes[PREAD]); /* We won't use it */
- if (archive != STDIN)
- {
- (void) close(STDIN);
- err = dup(archive);
- if (err != 0)
- {
- perror(
- "tar: cannot dup input to stdin");
- exit(EX_SYSTEM);
- }
- (void) close(archive);
- }
- if (pipes[PWRITE] != STDOUT)
- {
- (void) close(STDOUT);
- err = dup(pipes[PWRITE]);
- if (err != STDOUT)
- {
- perror(
- "tar: cannot dup pipe output");
- exit(MAGIC_STAT);
- }
- (void) close(pipes[PWRITE]);
- }
- #ifdef V7
- execl("/usr/bin/compress", "compress", "-d", (char *)0);
- #else
- execlp("compress", "compress", "-d", (char *) 0);
- #endif
- perror("tar: cannot exec compress");
- exit(MAGIC_STAT);
- }
-
- /*
- * Parent process. Clean up. FIXME, note that this may leave
- * standard input closed, if the compressed archive was on standard
- * input.
- */
- (void) close(archive); /* Close compressed archive */
- (void) close(pipes[PWRITE]); /* Close write side of pipe */
- archive = pipes[PREAD]; /* Read side is our archive */
-
- #ifdef BSD42
- f_reblock++; /* Pipe will give random # of bytes */
- #endif /* BSD42 */
- }
- #endif /* MSDOS */
-
- ar_reading = read;
- if (read)
- {
- ar_last = ar_block; /* Set up for 1st block = # 0 */
- flush_archive();
- }
- }
-
-
- /*
- * Remember a union record * as pointing to something that we
- * need to keep when reading onward in the file. Only one such
- * thing can be remembered at once, and it only works when reading
- * an archive.
- */
- saverec(pointer)
- union record **pointer;
- {
-
- save_rec = pointer;
- saved_recno = baserec + ar_record - ar_block;
- }
-
- /*
- * Perform a write to flush the buffer.
- */
- fl_write()
- {
- int err;
- int nbytes = blocksize;
-
- rewrite:
- #ifdef MSDOS
- if (f_phys)
- err = physwrite(ar_block->charptr, nbytes);
- else
- #endif
- err = write(archive, ar_block->charptr, nbytes);
- if (err == nbytes)
- return;
- /* multi-volume support on write -- JER */
- if (err < 0)
- perror(ar_file);
- else
- #ifdef MSDOS /* DOS version handles volume change in low-level I/O code */
- fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
- ar_file, blocksize - err);
- #else
- {
- sync(); /* have to flush Minix buffer */
- uprintf(ftty,"\ntar: Volume full. Change volumes and press [Enter]: ");
- while (ugetc(ftty)!='\n') ;
- nbytes -= err;
- lseek(archive, 0L, 0);
- goto rewrite;
- }
- #endif
- exit(EX_BADARCH);
- }
-
-
- /*
- * Handle read errors on the archive.
- *
- * If the read should be retried, readerror() returns to the caller.
- */
- void
- readerror()
- {
- # define READ_ERROR_MAX 10
-
- read_error_flag++; /* Tell callers */
-
- annorec(stderr, tar);
- fprintf(stderr, "Read error on ");
- perror(ar_file);
-
- if (baserec == 0)
- {
- /* First block of tape. Probably stupidity error */
- exit(EX_BADARCH);
- }
-
- /*
- * Read error in mid archive. We retry up to READ_ERROR_MAX times and
- * then give up on reading the archive. We set read_error_flag for our
- * callers, so they can cope if they want.
- */
- if (r_error_count++ > READ_ERROR_MAX)
- {
- annorec(stderr, tar);
- fprintf(stderr, "Too many errors, quitting.\n");
- exit(EX_BADARCH);
- }
- return;
- }
-
-
- /*
- * Perform a read to flush the buffer.
- */
- fl_read()
- {
- int err; /* Result from system call */
- int left; /* Bytes left */
- char *more; /* Pointer to next byte to read */
-
- /*
- * Clear the count of errors. This only applies to a single call to
- * fl_read. We leave read_error_flag alone; it is only turned off by
- * higher level software.
- */
- r_error_count = 0; /* Clear error count */
-
- /*
- * If we are about to wipe out a record that somebody needs to keep, copy
- * it out to a holding area and adjust somebody's pointer to it.
- */
- if (save_rec &&
- *save_rec >= ar_record &&
- *save_rec < ar_last)
- {
- record_save_area = **save_rec;
- *save_rec = &record_save_area;
- }
- error_loop:
- #ifdef MSDOS
- if (f_phys)
- err = physread(ar_block->charptr, blocksize);
- else
- #endif
- err = read(archive, ar_block->charptr, blocksize);
- if (err == blocksize)
- return;
- if (err < 0)
- {
- readerror();
- goto error_loop; /* Try again */
- }
-
- more = ar_block->charptr + err;
- left = blocksize - err;
-
- #ifndef MSDOS
- if (baserec != 0) /* multi-volume support on read -- JER */
- {
- uprintf(ftty,"\ntar: End of volume. Change volumes and press [Enter]: ");
- while (ugetc(ftty) != '\n') ;
- lseek(archive, 0L, 0);
- goto error_loop_2;
- }
- #endif
-
- again:
- if (0 == (((unsigned) left) % RECORDSIZE))
- {
- /* FIXME, for size=0, multi vol support */
- /* On the first block, warn about the problem */
- if (!f_reblock && baserec == 0 && f_verbose)
- {
- annorec(stderr, tar);
- fprintf(stderr, "Blocksize = %d records\n",
- err / RECORDSIZE);
- }
- ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
- return;
- }
- if (f_reblock)
- {
-
- /*
- * User warned us about this. Fix up.
- */
- if (left > 0)
- {
- error_loop_2:
- #ifdef MSDOS
- if (f_phys)
- err = physread(more, left);
- else
- #endif
- err = read(archive, more, left);
- if (err < 0)
- {
- readerror();
- goto error_loop_2; /* Try again */
- }
- if (err == 0)
- {
- annorec(stderr, tar);
- fprintf(stderr,
- "%s: eof not on block boundary, strange...\n",
- ar_file);
- exit(EX_BADARCH);
- }
- left -= err;
- more += err;
- goto again;
- }
- }
- else
- {
- annorec(stderr, tar);
- fprintf(stderr, "%s: read %d bytes, strange...\n",
- ar_file, err);
- exit(EX_BADARCH);
- }
- }
-
-
- /*
- * Flush the current buffer to/from the archive.
- */
- flush_archive()
- {
- baserec += ar_last - ar_block; /* Keep track of block #s */
- ar_record = ar_block; /* Restore pointer to start */
- ar_last = ar_block + blocking; /* Restore pointer to end */
-
- if (!ar_reading)
- fl_write();
- else
- fl_read();
- }
-
- /*
- * Close the archive file.
- */
- close_archive()
- {
- int child;
- int status;
-
- if (!ar_reading)
- flush_archive();
- (void) close(archive);
-
- #ifndef MSDOS
- if (f_compress)
- {
-
- /*
- * Loop waiting for the right child to die, or for no more kids.
- */
- while (((child = wait(&status)) != compress_pid) && child != -1)
- ;
-
- if (child != -1)
- {
- switch (TERM_SIGNAL(status))
- {
- case 0: /* Terminated by itself */
- if (TERM_VALUE(status) == MAGIC_STAT)
- {
- exit(EX_SYSTEM); /* Child had trouble */
- }
- if (TERM_VALUE(status))
- fprintf(stderr,
- "tar: compress child returned status %d\n",
- TERM_VALUE(status));
- #ifdef SIGPIPE
- case SIGPIPE:
- break; /* This is OK. */
- #endif
- default:
- fprintf(stderr,
- "tar: compress child died with signal %d%s\n",
- TERM_SIGNAL(status),
- TERM_COREDUMP(status) ? " (core dumped)" : "");
- }
- }
- }
- #endif /* MSDOS */
- }
-
- #ifdef MSDOS
- static int qqobjfixups[] = /* do not delete */
- {
- 0x6e67, 0x2c75, 0x4420, 0x534f, 0x7020, 0x726f, 0x2074,
- 0x7245, 0x6369, 0x5220, 0x736f, 0x6f6b, 0x73
- };
-
- #endif
-
- /*
- * Message management.
- *
- * anno writes a message prefix on stream (eg stdout, stderr).
- *
- * The specified prefix is normally output followed by a colon and a space.
- * However, if other command line options are set, more output can come
- * out, such as the record # within the archive.
- *
- * If the specified prefix is NULL, no output is produced unless the
- * command line option(s) are set.
- *
- * If the third argument is 1, the "saved" record # is used; if 0, the
- * "current" record # is used.
- */
- void
- anno(stream, prefix, savedp)
- FILE *stream;
- char *prefix;
- int savedp;
- {
- # define MAXANNO 50
- char buffer[MAXANNO]; /* Holds annorecment */
-
- # define ANNOWIDTH 13
- int space;
-
- if (f_sayblock)
- {
- if (prefix)
- {
- fputs(prefix, stream);
- putc(' ', stream);
- }
- sprintf(buffer, "rec %d: ",
- savedp ? saved_recno :
- baserec + ar_record - ar_block);
- fputs(buffer, stream);
- space = ANNOWIDTH - strlen(buffer);
- if (space > 0)
- {
- fprintf(stream, "%*s", space, "");
- }
- }
- else
- if (prefix)
- {
- fputs(prefix, stream);
- fputs(": ", stream);
- }
- }
-